package com.jt.auth.config;

import com.sun.deploy.services.DefaultService;
import lombok.AllArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices;
import org.springframework.security.oauth2.provider.token.DefaultTokenServices;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;

/**
 * Oauth2是一种协议或规范，它定义了完成用户身份认证和授权的方式，例如基于密码进行认证，
 * 基于第三方令牌认证(例如QQ，微信登录)等，但具体的完成过程需要有一组对象，这些对象的
 * 构成，一般有如下几部分：
 * 0)系统数据服务器(类似数据库，文件系统 ⬇ )
 * 1)资源服务器(负责访问资源，例如:商品，订单，库存，会员。。。)
 * 2)认证服务器(负责完成用户身份的认证)
 * 3)客户端对象(表单，令牌，)
 * 4)资源拥有者(用户)
 *
 * 思考：在Oauth2这种规范下，如何对用户身份进行认证？
 * 1)认证的地址？(让用户去哪儿里认证)
 * 2)用户需要携带什么信息去认证？(办理)
 * 3)具体完成认证的对象是谁？
 * 这些规范，在Oauth2中有默认定义，但是业务不同，需要有不同实现方式
 */
@AllArgsConstructor  //标识全参构造
@Configuration  //标识配置类
@EnableAuthorizationServer //开启认证和授权
public class Oauth2Config extends AuthorizationServerConfigurerAdapter{
    //@Autowired
    private PasswordEncoder passwordEncoder;
    //@Autowired
    private AuthenticationManager authenticationManager;
    //@Autowired
    private TokenStore tokenStore;
    //@Autowired
    private JwtAccessTokenConverter jwtAccessTokenConverter;


    /**
     * 在此方法中公开认证地址(客户端认证时输入的path是谁)
     * @param security
     * @throws Exception
     */
    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
        //super.configure(security);
        //对外发布认证入口(默认地址->/path/token)
        security.tokenKeyAccess(("permitAll()"))
                //公开校验令牌的地址(/auth/token)
                .checkTokenAccess("permitAll()")
                //允许通过表单方式进行认证
                .allowFormAuthenticationForClients();
    }

    /**
     * 定义客户端应该携带什么信息去认证？
     * 指明哪些对象可以到这里进行认证(哪个客户端对象需要什么特点)。
     * @param clients
     * @throws Exception
     */
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
//        super.configure(clients);
        clients.inMemory()
                //客户端标识
                .withClient("gateway-client")
                //客户端携带密钥  123456
                .secret(passwordEncoder.encode("123456"))
                //定义认证类型()
                .authorizedGrantTypes("password","refresh_token")
                //作用域，满足如上
                .scopes("all");
    }

    /**
     * 定义由谁完成认证,认证成功后以怎样形式颁发令牌
     * @param endpoints
     * @throws Exception
     */
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
//        super.configure(endpoints);
        //定义由谁完成认证
        endpoints.authenticationManager(authenticationManager);
        //定义令牌的存储(存储到内存,mysql,redis,jwt令牌中)
//        endpoints.tokenStore(tokenStore());
        //endpoints.tokenEnhancer(jwtAccessTokenConverter());//令牌增强
        //定义由谁完成认证
        endpoints.authenticationManager(authenticationManager);
        //定义令牌业务处理对象
        endpoints.tokenServices(tokenServices());
        endpoints.allowedTokenEndpointRequestMethods(HttpMethod.GET,HttpMethod.POST);
    }

    /**
     * tokenServices对象，用于处理token对象
     * @return
     */
    @Bean
    public AuthorizationServerTokenServices tokenServices() {
        DefaultTokenServices tokenServices = new DefaultTokenServices();
        //设置令牌存储对象
        tokenServices.setTokenStore(tokenStore);
        //设置令牌增强()
        tokenServices.setTokenEnhancer(jwtAccessTokenConverter);
        //设置访问令牌有效期
        tokenServices.setAccessTokenValiditySeconds(3600);
        //设置刷新令牌(在访问令牌即将到期时，还可以使用刷新令牌再去请求一个新令牌)
        tokenServices.setSupportRefreshToken(true);
        //设置刷新令牌有效期(这个时间要比访问时间长一点)
        tokenServices.setRefreshTokenValiditySeconds(7200);
        return tokenServices;
    }

}
